package indi.mozping.nio.server1;

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @author by mozping
 * @Classname NioServerTest
 * @Description NioServerTest
 * @Date 2019/11/27 9:30
 */
public class NioServerTest {

    private static final int[] PORTS = new int[]{12345, 12346, 12347};

    public static void main(String[] args) throws Exception {

        //1.创建一个Selector
        Selector selector = Selector.open();

        for (int port : PORTS) {
            //2.创建 serverSocketChannel，注册到 selector 选择器 , 设置非阻塞模式
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            //3.端口绑定，通过 ServerSocketChannel 关联的 ServerSocket 绑定
            ServerSocket serverSocket = serverSocketChannel.socket();
            serverSocket.bind(new InetSocketAddress(port));
        }

        while (true) {
            //4.select 是阻塞方法,有事件就返回
            int num = selector.select();
            //5.获取事件,可能多个通道有事件，因此返回的是一个集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                //可接受事件
                if (selectionKey.isAcceptable()) {
                    //拿到channel对象

                    int interestOps = selectionKey.interestOps();
                    int readyOps = selectionKey.readyOps();
                    System.out.println("兴趣事件值：" + interestOps);
                    System.out.println("就绪事件值：" + readyOps);

                    ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();

                    //得到 SocketChannel ，代表 TCP 连接对象
                    SocketChannel socketChannel = channel.accept();
                    System.out.println("服务端处理端口：" + socketChannel.socket().getPort());
                    //配置非阻塞，由此可以看到客户端 socketChannel 也可以是非阻塞的，
                    // configureBlocking 方法实际上定义在父类，因此客户端服务端都是非阻塞的
                    socketChannel.configureBlocking(false);

                    //也把连接对象注册到selector,连接对象关心的应该是读写事件
                    socketChannel.register(selector, SelectionKey.OP_READ);

                    //移除非常关键,因此这个连接事件已经处理了，不移除的话会多次处理
                    iterator.remove();
                    System.out.println("获取到客户端的连接: " + socketChannel);
                } else if (selectionKey.isReadable()) {
                    int interestOps = selectionKey.interestOps();
                    int readyOps = selectionKey.readyOps();
                    System.out.println("兴趣事件值：" + interestOps);
                    System.out.println("就绪事件值：" + readyOps);

                    //拿到channel对象
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(512);
                    int readBytes = socketChannel.read(byteBuffer);
                    if (readBytes > 0) {
                        byteBuffer.flip();
                        byte[] bytes = new byte[byteBuffer.remaining()];
                        byteBuffer.get(bytes);
                        String body = new String(bytes, "UTF-8");
                        System.out.println("服务端收到消息 : " + body);
                        //将消息写回客户端
                        byte[] resp = body.getBytes();
                        ByteBuffer write = ByteBuffer.allocate(body.getBytes().length);
                        write.put(resp).flip();
                        socketChannel.write(write);
                    }
                    iterator.remove();
                }
            }
        }
    }
}